package cc.shacocloud.mirage.restful;

import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
import org.jetbrains.annotations.Nullable;

import java.util.Map;
import java.util.function.Supplier;

/**
 * 路由请求上下文
 */
public interface RoutingContext {
    
    /**
     * mirage web 框架内部参数前缀
     */
    String INTERNAL_PARAMETERS_PREFIX = ".mirage_web_";
    
    /**
     * 接收请求时间 key，使用 {@link #get(String)} 获取
     */
    String RECEIVE_REQUEST_TIME = INTERNAL_PARAMETERS_PREFIX + "receiveRequestTime";
    
    /**
     * 当前请求挂载的拦截器，使用 {@link #get(String)} 获取
     */
    String REQUEST_INTERCEPTORS = INTERNAL_PARAMETERS_PREFIX + "requestInterceptors";
    
    /**
     * @return {@link Vertx}。与此上下文的初始化{@link Router}相关联的实例
     */
    Vertx vertx();
    
    /**
     * @return HTTP请求对象
     */
    HttpRequest request();
    
    /**
     * @param request 自定义请求体
     * @return HTTP请求对象
     */
    HttpRequest request(HttpRequest request);
    
    /**
     * @return HTTP响应对象
     */
    HttpResponse response();
    
    /**
     * @param response 自定义响应体
     * @return HTTP响应对象
     */
    HttpResponse response(HttpResponse response);
    
    /**
     * 在上下文中放置一些任意的数据。这将在任何接收上下文的处理程序中可用。
     *
     * @param key 数据的键
     * @param obj 的数据
     * @return 引用，这样API就可以流畅地使用
     */
    RoutingContext put(String key, Object obj);
    
    /**
     * 从上下文获取一些数据。数据可在任何接收上下文的处理程序中使用。
     *
     * @param key 数据的键
     * @param <T> 数据的类型
     * @return 数据
     * @throws ClassCastException 如果数据不是预期的类型
     */
    @Nullable <T> T get(String key);
    
    /**
     * 从上下文获取一些数据。数据可在任何接收上下文的处理程序中使用。
     *
     * @param key      数据的键
     * @param supplier 如果在上下文中值不存在则使用该函数提供处理
     * @param <T>      数据的类型
     * @return 数据
     * @throws ClassCastException 如果数据不是预期的类型
     */
    <T> T get(String key, Supplier<T> supplier);
    
    /**
     * 从上下文中删除一些数据。数据可在任何接收上下文的处理程序中使用。
     *
     * @param key 数据的键
     * @param <T> 数据的类型
     * @return 与键关联的先前数据
     * @throws ClassCastException 如果数据不是预期的类型
     */
    <T> T remove(String key);
    
    /**
     * @return 所有上下文数据都作为一个映射
     * @see #put(String, Object)
     */
    Map<String, Object> data();
    
    /**
     * 返回请求的标准化路径。
     * <p>
     * 如果访问客户端请求的服务器资源，建议始终使用规范化的路径，而不是{@link HttpRequest#path()}
     *
     * @return 正常路径
     */
    String normalizedPath();
    
    /**
     * 使用新路径重新启动当前路由器，并重用原来的方法。然后所有路径参数都被解析，并在params列表中可用。查询参数也将被允许和可用。
     *
     * @param path 新的http路径。
     */
    default void reroute(String path) {
        reroute(request().method(), path);
    }
    
    /**
     * 用新的方法和路径重新启动当前路由器。然后，所有路径参数都被解析并在 params列表中可用。查询参数也将被允许和可用。
     *
     * @param method 新的http请求
     * @param path   新的http路径
     */
    void reroute(HttpMethod method, String path);
    
    /**
     * 告诉路由器将这个上下文路由到下一个匹配的路由(如果有的话)。
     * <p>
     * 如果一个处理程序没有调用next，那么处理程序应该确保它结束响应，否则没有响应将被发送。
     */
    void next();
    
    /**
     * 为请求/响应上下文添加一个结束处理程序。
     * <p>
     * 当响应被释放或遇到异常时，将调用此函数以允许一致的清理，或当客户端接收到响应时异步调用处理程序。
     *
     * @param handler 将被调用并有成功或失败结果的处理程序。
     * @return 处理程序的id。如果您稍后想要删除处理程序，可以使用此方法。
     */
    int addEndHandler(Handler<AsyncResult<Void>> handler);
    
    /**
     * 删除一个结束处理程序
     *
     * @param handlerId 从{@link #addEndHandler}返回的id。
     * @return 如果处理程序存在并被删除，则为true，否则为false
     */
    boolean removeEndHandler(int handlerId);
    
    /**
     * 获取源对象 {@link RoutingContext}
     */
    io.vertx.ext.web.RoutingContext getSource();
    
    
}
